package cn.edu.neu.lab603.cloudeval.monitor

import cn.edu.neu.lab603.cloudeval.CloudEval
import cn.edu.neu.lab603.cloudeval.entity.CloudTask.SubmissionEvent
import cn.edu.neu.lab603.cloudeval.entity.DataCenter.ShutdownEvent
import cn.edu.neu.lab603.cloudeval.entity.{CloudTask, DataCenter}
import cn.edu.neu.lab603.des.{SimMetric, SimMonitor}

import scala.collection.mutable

/** SLA 评价指标的抽象类。
  *
  * Created by yfwz100 on 2016/11/4.
  */
class CloudletSLAMetric extends SimMonitor[CloudEval] {

  /** 平均 SLA 违反情况。 */
  lazy val averageSLAMetric = new SimMetric[Double] {

    override def getCurrentValue: Double = finishTimeMap.map { e =>
      val (cloudletId, finishTime) = e
      val len = CloudTask.get(cloudletId).length.toDouble
      val actualLen = finishTime - submissionTimeMap(cloudletId)
      (actualLen - len) / actualLen
    }.sum / finishTimeMap.size

  }

  /** 全局 SLA 违反情况。 */
  lazy val overallSLAMetric = new SimMetric[Double] {

    override def getCurrentValue: Double = finishTimeMap.map { e =>
      val (cloudletId, finishTime) = e
      finishTime - submissionTimeMap(cloudletId)
    }.sum.toDouble / cloudletLengthMap.values.sum - 1

  }

  def getSLAInfo: Map[Long, (Long, Int)] = finishTimeMap.map { e =>
    val (cloudletId, finishTime) = e
    val len = CloudTask.get(cloudletId).length
    (cloudletId, (finishTime - submissionTimeMap(cloudletId), len))
  }.toMap

  def size: Int = finishTimeMap.size

  private lazy val submissionTimeMap = new mutable.HashMap[Long, Long]
  private lazy val finishTimeMap = new mutable.HashMap[Long, Long]
  private lazy val cloudletLengthMap = new mutable.HashMap[Long, Long]

  /** 是否监听该任务的事件。
    *
    * @param context 执行上下文
    * @return 布尔值
    */
  override def isMatch(context: CloudEval): Boolean = context.getCurrentTask match {
    case _: CloudTask.SubmissionEvent => true
    case _: CloudTask.FinishEvent => true
    case _: DataCenter.ShutdownEvent => true
    case _ => false
  }

  /** 任务执行前执行的内容。
    *
    * @param context 执行上下文
    */
  override def before(context: CloudEval): Unit = {
  }

  /** 任务执行后执行的内容。
    *
    * @param context 执行上下文
    */
  override def after(context: CloudEval): Unit = {
    implicit val simulation = context
    val clock = context.getCurrentClock
    context.getCurrentTask match {
      case _: CloudTask.SubmissionEvent =>
        for (cloudlet <- SubmissionEvent.getCurrentCloudlets) {
          if (!submissionTimeMap.contains(cloudlet.cloudletNo)) {
            submissionTimeMap(cloudlet.cloudletNo) = clock
          }
        }
      case task: CloudTask.FinishEvent =>
        finishTimeMap(task.cloudlet.cloudletNo) = clock
        cloudletLengthMap(task.cloudlet.cloudletNo) = task.cloudlet.length
      case _: ShutdownEvent =>
        submissionTimeMap.foreach { p =>
          if (!(finishTimeMap contains p._1)) {
            finishTimeMap(p._1) = clock
            cloudletLengthMap(p._1) = CloudTask.get(p._1).getCursor
          }
        }
    }
  }

}


